Skip to content

CMake unity build#1982

Merged
nullsystem merged 4 commits into
NeotokyoRebuild:masterfrom
nullsystem:GH-1981_CMakeUnityBuild
Jun 11, 2026
Merged

CMake unity build#1982
nullsystem merged 4 commits into
NeotokyoRebuild:masterfrom
nullsystem:GH-1981_CMakeUnityBuild

Conversation

@nullsystem

@nullsystem nullsystem commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

Description

  • Improve compile times, no ccache full compile (these are just sample times to give a rough idea of the speedups, it can varies):
    • Linux debug 9800x3d: 1m13s to 23s (3.2x speedup)
    • Linux release 9800x3d: 1m36s to 1m21s (1.2x speedup)
    • Windows debug 9800x3d: 1m21s to 14s (4.7x speedup)
    • Windows release 9800x3d: 1m24s to 18s (4.6x speedup)
    • Time VS in CMake library build stage: https://github.com/NeotokyoRebuild/neo/actions/runs/27134842462 (non-unity) VS https://github.com/NeotokyoRebuild/neo/actions/runs/27227387768 (unity)
    • Linux debug github runner: 12m16s to 2m6s (5.8x speedup) | Total down from 16m16s to 5m27s
    • Linux release github runner: 14m3s to 4m57s (2.8x speedup) | Total down from 18m16s to 9m41s
    • Windows debug github runner: 10m56s to 1m24s (7.8x speedup) | Total down from 14m17s to 4m58s
    • Windows release github runner: 11m22s to 2m38s (4.3x speedup) | Total down from 14m45s to 6m5s
  • Lots of files not unity build due to IInput vs vgui::IInput ambiguity
  • Some files left out cause easier to leave it out than dealing with it
  • Not doing materialsystem as it .inc relies on 1-file-per-obj
  • Few files left out of unity due to issues shown up in runtime
  • Object/compilation units from 1.4k to ~166

Toolchain

  • Windows MSVC VS2022
  • Linux GCC Distro Native Arch/GCC 16

Linked Issues

@nullsystem nullsystem added Build System CMake and other build-related stuff CI Continuous Integration (GitHub Actions) labels Jun 7, 2026
@nullsystem nullsystem marked this pull request as draft June 7, 2026 18:10
@Masterkatze Masterkatze self-requested a review June 7, 2026 18:22
@nullsystem nullsystem force-pushed the GH-1981_CMakeUnityBuild branch 17 times, most recently from b6ae425 to c3aeb29 Compare June 8, 2026 19:40
@nullsystem nullsystem changed the title [WIP] cmake unity build cmake unity build Jun 8, 2026
@nullsystem nullsystem marked this pull request as ready for review June 8, 2026 21:25
@nullsystem nullsystem force-pushed the GH-1981_CMakeUnityBuild branch from c3aeb29 to 2d1e488 Compare June 8, 2026 21:39
@Masterkatze Masterkatze changed the title cmake unity build CMake unity build Jun 9, 2026
@Rainyan

Rainyan commented Jun 9, 2026

Copy link
Copy Markdown
Member

I'm seeing a crash with Windows MSVC 2022 17.14.33 (latest stable currently), doing a fresh build with first using git clean -xdff to clean up the workspace, and then following the usual setup: https://github.com/nullsystem/neo/tree/GH-1981_CMakeUnityBuild#visual-studio-2022-windows and using this launch.vs.json file.

After the build, when attaching in debug mode, first there are these 2 assertions failing:

First assertion failure:

locals:
+		pClassName	0x000002075236d6c8 "BASECOMBATWEAPON_DERIVED_FROM"	const char *
+		pClassNameMatch	0x000002075236d698 "CBaseAnimating"	const char *

>	server.dll!InternalCheckDeclareClass(const char * pClassName, const char * pClassNameMatch, void * pTestPtr, void * pBasePtr) Line 44	C++
 	server.dll!CBaseAnimating::CheckDeclareClass(const char * pShouldBe) Line 33	C++
 	server.dll!CheckDeclareClass_Access<CBaseAnimating>(CBaseAnimating * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CBaseCombatWeapon::CheckDeclareClass(const char * pShouldBe) Line 157	C++
 	server.dll!CheckDeclareClass_Access<CBaseCombatWeapon>(CBaseCombatWeapon * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CWeaponHL2MPBase::CheckDeclareClass(const char * pShouldBe) Line 35	C++
 	server.dll!CheckDeclareClass_Access<CWeaponHL2MPBase>(CWeaponHL2MPBase * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CBaseHL2MPCombatWeapon::CheckDeclareClass(const char * pShouldBe) Line 26	C++
 	server.dll!CheckDeclareClass_Access<CBaseHL2MPCombatWeapon>(CBaseHL2MPCombatWeapon * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CNEOBaseCombatWeapon::CheckDeclareClass(const char * pShouldBe) Line 118	C++
 	server.dll!CheckDeclareClass_Access<CNEOBaseCombatWeapon>(CNEOBaseCombatWeapon * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CWeaponAA13::CheckDeclareClass(const char * pShouldBe) Line 15	C++
 	server.dll!CheckDeclareClass_Access<DT_WeaponAA13::ignored>(DT_WeaponAA13::ignored * __formal, const char * pIgnored) Line 20	C++
 	server.dll!DT_WeaponAA13::`dynamic initializer for 'verifyDeclareClass''() Line 20	C++
 	server.dll!_initterm(void(*)() * first, void(*)() * last) Line 22	C++
 	[External Code]	

The second assertion failure:

locals:
+		pClassName	0x000002075236d6c8 "BASECOMBATWEAPON_DERIVED_FROM"	const char *
+		pClassNameMatch	0x000002075236d698 "CBaseAnimating"	const char *

>	server.dll!InternalCheckDeclareClass(const char * pClassName, const char * pClassNameMatch, void * pTestPtr, void * pBasePtr) Line 44	C++
 	server.dll!CBaseAnimating::CheckDeclareClass(const char * pShouldBe) Line 33	C++
 	server.dll!CheckDeclareClass_Access<CBaseAnimating>(CBaseAnimating * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CBaseCombatWeapon::CheckDeclareClass(const char * pShouldBe) Line 157	C++
 	server.dll!CheckDeclareClass_Access<CBaseCombatWeapon>(CBaseCombatWeapon * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CWeaponHL2MPBase::CheckDeclareClass(const char * pShouldBe) Line 35	C++
 	server.dll!CheckDeclareClass_Access<CWeaponHL2MPBase>(CWeaponHL2MPBase * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CBaseHL2MPCombatWeapon::CheckDeclareClass(const char * pShouldBe) Line 26	C++
 	server.dll!CheckDeclareClass_Access<CBaseHL2MPCombatWeapon>(CBaseHL2MPCombatWeapon * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CNEOBaseCombatWeapon::CheckDeclareClass(const char * pShouldBe) Line 118	C++
 	server.dll!CheckDeclareClass_Access<CNEOBaseCombatWeapon>(CNEOBaseCombatWeapon * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CWeaponBALC::CheckDeclareClass(const char * pShouldBe) Line 25	C++
 	server.dll!CheckDeclareClass_Access<DT_WeaponBALC::ignored>(DT_WeaponBALC::ignored * __formal, const char * pIgnored) Line 19	C++
 	server.dll!DT_WeaponBALC::`dynamic initializer for 'verifyDeclareClass''() Line 19	C++
 	server.dll!_initterm(void(*)() * first, void(*)() * last) Line 22	C++
 	[External Code]	

And shortly after it crashes with a call stack overflow, at:

locals:
+		pShouldBe	0x0000020752371420 "CNEOBaseProjectile"	const char *

>	server.dll!CNEOBaseProjectile::CheckDeclareClass(const char * pShouldBe) Line 9	C++
 	server.dll!CheckDeclareClass_Access<CNEOBaseProjectile>(CNEOBaseProjectile * __formal, const char * pShouldBe) Line 53	C++
 	
    (The same call stack pattern of `CheckDeclareClass_Access` -> `CheckDeclareClass`
    repeats very many times here until the stack overflow.)
    
 	server.dll!CheckDeclareClass_Access<CNEOBaseProjectile>(CNEOBaseProjectile * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CNEOBaseProjectile::CheckDeclareClass(const char * pShouldBe) Line 9	C++
 	server.dll!CheckDeclareClass_Access<CNEOBaseProjectile>(CNEOBaseProjectile * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CNEOBaseProjectile::CheckDeclareClass(const char * pShouldBe) Line 9	C++
 	server.dll!CheckDeclareClass_Access<CNEOBaseProjectile>(CNEOBaseProjectile * __formal, const char * pShouldBe) Line 53	C++
 	server.dll!CWeaponDetpack::CheckDeclareClass(const char * pShouldBe) Line 25	C++
 	server.dll!CheckDeclareClass_Access<DT_WeaponDetpack::ignored>(DT_WeaponDetpack::ignored * __formal, const char * pIgnored) Line 30	C++
 	server.dll!DT_WeaponDetpack::`dynamic initializer for 'verifyDeclareClass''() Line 30	C++
 	server.dll!_initterm(void(*)() * first, void(*)() * last) Line 22	C++
 	[External Code]	

With the DECLARE_CLASS macro expanding to something like:

typedef CNEOBaseProjectile BaseClass; typedef CNEOBaseProjectile ThisClass; template <typename T> friend int CheckDeclareClass_Access(T*, const char* pShouldBe); static int CheckDeclareClass(const char* pShouldBe) {
	InternalCheckDeclareClass(pShouldBe, "CNEOBaseProjectile", (ThisClass*)0xFFFFF, (BaseClass*)(ThisClass*)0xFFFFF); return CheckDeclareClass_Access((BaseClass*)0, "CNEOBaseProjectile");
};

@Rainyan

Rainyan commented Jun 9, 2026

Copy link
Copy Markdown
Member

Also tested on Linux Pop!_OS 24.04 LTS, with gcc 13.3.0 and cmake 2.28.3, and the Linux build did not have these problems.

@Rainyan

Rainyan commented Jun 9, 2026

Copy link
Copy Markdown
Member

And just as a build-environment sanity check, I can also verify seeing this Windows crash when attaching to the GH CI generated libraries libraries-Windows-Debug from this CI run from the DL link: https://github.com/NeotokyoRebuild/neo/actions/runs/27168661804/artifacts/7493167635

@nullsystem nullsystem force-pushed the GH-1981_CMakeUnityBuild branch from 0cff67b to 81429d6 Compare June 9, 2026 18:16
@nullsystem

nullsystem commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator Author

@Rainyan I've decided to just taken the weapons part from the unity grouping, seems those macros relies on file separation and that fixes the asserts.

@nullsystem nullsystem force-pushed the GH-1981_CMakeUnityBuild branch from 81429d6 to 0864bd5 Compare June 9, 2026 18:32
@nullsystem

nullsystem commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator Author

@Rainyan Actually I've revised it now, the weapon's unity grouping will be kept but just a few more files kept out: 0864bd5 and the asserts no longer happens

@Rainyan Rainyan self-requested a review June 10, 2026 10:20
@Rainyan

This comment was marked as resolved.

@Rainyan

This comment was marked as resolved.

@nullsystem nullsystem force-pushed the GH-1981_CMakeUnityBuild branch from 1e59217 to 0864bd5 Compare June 10, 2026 18:04
@nullsystem

Copy link
Copy Markdown
Collaborator Author

@Rainyan Actually I've revised it now, the weapon's unity grouping will be kept but just a few more files kept out: 0864bd5 and the asserts no longer happens

To verify, is it correct for the CHECK_DECLARE_CLASS to now no longer do the assertions at all for Windows? Trying to breakpoint into them from a debug build, it's not being hit:

breakpoint And if naively doing a `#define VALIDATE_DECLARE_CLASS` just above that block, then it is hitting the assertions, and failing with a crash again: Screenshot 2026-06-10 134324 Is this the intended behaviour? Granted if there is no simple fix for the assert behaviour, then IMO it's better to take the faster compile times benefit, and give up on having this runtime check. In which case I guess this is fine?

But in any case, I can verify that both Debug and Release builds for Windows are now compiling successfully for me, and they seem to be working just fine in runtime.

@Rainyan I think VALIDATE_DECLARE_CLASS would also be not defined for the non-unity builds, so I think this may actually be the intended behavior after all. Which in this case I guess leaving as it is is fine and having the define cross from a base-weapon source file was the issue after all.

Rainyan
Rainyan previously approved these changes Jun 11, 2026
Comment thread src/game/server/triggers.cpp
* Improve compile times, tested no ccache full compile:
    * Linux debug 9800x3d: 1m13s to 23s (3.2x speedup)
    * Linux release 9800x3d: 1m36s to 1m21s (1.2x speedup)
    * Windows debug 9800x3d: 1m21s to 13s (6.2x speedup)
    * Windows release 9800x3d: 1m24s to 18s (4.6x speedup)
    * Time VS in CMake library build stage:
	* Non-unity: https://github.com/NeotokyoRebuild/neo/actions/runs/27134842462
	* Unity: https://github.com/NeotokyoRebuild/neo/actions/runs/27162359918
    * Linux debug github runner: 12m16s to 2m5s (5.8x speedup)
    * Linux release github runner: 14m3s to 5m4s (2.8x speedup)
    * Windows debug github runner: 10m56s to 1m30s (7.2x speedup)
    * Windows release github runner: 11m22s to 2m6s (5.3x speedup)
* From ~1.4k obj to 150-166
* Lots of files not unity build due to IInput vs vgui::IInput
  ambiguity
* Some files left out cause easier to leave it out than dealing
  with it
* Not doing materialsystem as it .inc relies on 1-file-per-obj
* Few files left out of unity due to issues shown up in runtime
* Windows cmd to measure:
    * powershell -Command "Measure-Command {cmake --build --preset
      windows-[debug/release] | Out-Default}"

* fixes NeotokyoRebuild#1981
@nullsystem nullsystem merged commit ae93dc6 into NeotokyoRebuild:master Jun 11, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Build System CMake and other build-related stuff CI Continuous Integration (GitHub Actions)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CMake unity build

3 participants